#!/usr/bin/python
import init_pkg_db
import util
from package.models import Module, PackageName, Package, FixedBug, ChangedPackage, Dependancy, OSRelease

import os
import sys

from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
class Command(BaseCommand):
    task = 'update_build'
    option_list = BaseCommand.option_list + (
        make_option('-o',
            '--os_release',
            action='store',
            type='string',
            dest=util.OS_RELEASE,
            help='designate the os release. eg. \'s11u2\' or \'s12\''),
        make_option('-b',
            '--build',
            action='store',
            type='string',
            dest=util.BUILD,
            help='designate the build like \'04\' or \'16\''),
    )
    
    #clear all packages of current build in db
    def clear_build(self):
        os_release,created = OSRelease.objects.get_or_create(name=self.os_release_str)
        Package.objects.filter(os_release=os_release, change_in_build=self.current_build).delete()
        

    def add_one_pkg(self, pkg_dict):
        #ignore packages flagged 'o' or 'r', though it is new.
        if 'n' == pkg_dict[util.FLAG]:
            pkg_dict[util.FLAG] = 'a'
            util.create_package(self.task, pkg_dict, self.os_release_str, self.current_build)

    def del_one_pkg(self, pkg_dict):
        #flag deleted package with 'm'
        if 'n' == pkg_dict[util.FLAG]:
            pkg_dict[util.FLAG] = 'm'
            util.create_package(self.task, pkg_dict, self.os_release_str, self.current_build)

    def process_diff_a(self, new_lines):
        for new_line in new_lines:
            self.add_one_pkg(new_line)
    
    def process_diff_d(self, old_lines):
        for old_line in old_lines:
            self.del_one_pkg(old_line)

    def process_diff_c(self, old_lines, new_lines):
        for new_line in new_lines:
            #use Name as condictions
            condictions = {util.NAME:new_line.get(util.NAME)}
            #find the matched old one in last build
            old_line = util.find_one_from_dicts(old_lines, condictions)
            #if old one not exist, it is a new line, which means it is a new package
            if not old_line:
                self.add_one_pkg(new_line)
            #old one exists
            else:
                old_line_flag = old_line.get(util.FLAG)
                if 'n' == old_line_flag:
                    new_line_flag = new_line.get(util.FLAG)
                    old_category = old_line.get(util.CATEGORY)
                    new_category = new_line.get(util.CATEGORY)
                    if 'o' == new_line_flag or 'r' == new_line_flag:
                        #Flagged 'o' or 'r' pacakges have no category, assign the old one's
                        new_line[util.CATEGORY] = old_category
                    elif 'n' == new_line_flag and old_category == new_category:
                        #both are flagged 'n' indicates only the version changed
                        new_line[util.FLAG] = 'v'
                    else:
                        util.error_log(self.task,
                            'unexpeced diff info: {0} in last build, {1} in current build'.format(old_line.__str__(), new_line.__str__()),
                            util.pkg_log_info.format(name_str, self.os_release_str, self.current_build)
                        )
                        continue
                    util.create_package(self.task, new_line, self.os_release_str, self.current_build)
                #if last build is flaged 'o' or 'r', we take it as a new package
                elif 'o' == old_line_flag or 'r' == old_line_flag:
                    self.add_one_pkg(new_line)
                else:
                    util.error_log(self.task,
                        'unexpeced diff info: {0} in last build, {1} in current build'.format(old_line.__str__(), new_line.__str__()),
                        util.pkg_log_info.format(name_str, self.os_release_str, self.current_build)
                    )
                    continue

    def get_num_from_mark(self, mark):
        if ',' in mark:
            left, right = mark.split(',')
            return int(right) - int(left) + 1
        else:
            return 1

    def process_diff(self, data):
        lines = data.splitlines()
        line_num = 0
        while line_num < len(lines):
            if 'c' in lines[line_num]:
                left_mark,right_mark = lines[line_num].split('c')
                origin_num = self.get_num_from_mark(left_mark)
                origin_lines = origin_num * [None]
                #get original lines in last build
                for i in range(origin_num):
                    line_num+=1
                    origin_lines[i] = util.get_dict_from_line(lines[line_num])
                line_num+=1 #skip the delimiter '-----'
                new_num = self.get_num_from_mark(right_mark)
                new_lines = new_num * [None]
                #get new lines in current build
                for i in range(new_num):
                    line_num+=1
                    new_lines[i] = util.get_dict_from_line(lines[line_num])
                self.process_diff_c(origin_lines, new_lines)
            elif 'a' in lines[line_num]:
                left_mark,right_mark = lines[line_num].split('a')
                new_num = self.get_num_from_mark(right_mark)
                new_lines = new_num * [None]
                for i in range(new_num):
                    line_num+=1
                    new_lines[i] = util.get_dict_from_line(lines[line_num])
                self.process_diff_a(new_lines)
            elif 'd' in lines[line_num]:
                left_mark,right_mark = lines[line_num].split('d')
                new_num = self.get_num_from_mark(left_mark)
                new_lines = new_num * [None]
                for i in range(new_num):
                    line_num+=1
                    new_lines[i] = util.get_dict_from_line(lines[line_num])
                self.process_diff_d(new_lines)
            line_num+=1

    def handle(self, *args, **options):
        self.os_release_str = options[util.OS_RELEASE]
        self.current_build = options[util.BUILD]
        if not self.os_release_str or not self.current_build:
            sys.stderr.write('build or os_release option not given\n')
            util.halt(self.task)
        self.last_build =  util.get_last_build(self.os_release_str, self.current_build)
        #if last build does not exist
        if not self.last_build:
            util.error_log(self.task, 'Last build Not Found', util.build_log_info.format(self.os_release_str, self.last_build))
            util.halt(self.task)
        last_file= util.pkg_list_file.format(self.os_release_str, self.last_build)
        current_file = util.pkg_list_file.format(self.os_release_str, self.current_build)
        #diff last build and current build
        stdout, stderr = util.run_bash('diff {0} {1}'.format(last_file, current_file))
        if stderr:
            util.error_log(self.task, 'diff failed:' + stderr.strip(), util.build_log_info.format(self.os_release_str, self.current_build))
            util.halt(self.task)
        if stdout:
            self.clear_build()
            self.process_diff(stdout)
        else:
            print('packages in {0} build {1} is the same as the last build.\n'.format(self.os_release_str, self.current_build))
        util.log(self.task, 'success')
